from aiogram import Router, types, F
from aiogram.fsm.context import FSMContext
from loguru import logger as log

from database import models
from tg_bot.filters.user_filter import IsAdmin
from tg_bot.keyboards import inline, callback_factory
from tg_bot.services.text import not_correct_input
from tg_bot.states.admin import AdminProduct, AdminCategory
from .admin_category import category_info

dp = Router()
dp.message.filter(IsAdmin(is_admin=True))
dp.callback_query.filter(IsAdmin())


@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'add_product'
), AdminCategory.info)
async def add_product_call(call: types.CallbackQuery, state: FSMContext):
    text = 'Введите название продукта: '
    key = await inline.cancel()
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.add_name)


@dp.message(AdminProduct.add_name)
async def add_product_name(message: types.Message, state: FSMContext):
    data = await state.get_data()
    category: models.Category = data.get('category')
    if category.products.filter(name=message.text).count() > 0:
        await not_correct_input(message, 'Продукт с таким названием уже имеется в данной категории.')
    else:
        await message.answer(f'Введите цену для {message.text}: ',
                             reply_markup=await inline.cancel())
        await state.set_state(AdminProduct.add_price)
        await state.update_data({'name': message.text})


@dp.message(AdminProduct.add_price)
async def add_product_price(message: types.Message, state: FSMContext):
    data = await state.get_data()
    category: models.Category = data.get('category')
    name = data.get('name')
    try:
        models.Product.objects.create(
            category=category,
            name=name,
            price=float(message.text)
        )
        text = category.get_name()
        data = await state.get_data()
        current_page = data.get('ProductPaginator_page', 1)
        key = inline.ProductPaginator(
            dp,
            category=category,
            current_page=current_page,
            before_pagination_keyboards=[
                await inline.category_settings(),
                await inline.add_product()
            ],
            after_pagination_keyboards=[await inline.back('category_menu')]
        )
        await message.answer(text, reply_markup=key.as_markup())
        await state.set_state(AdminCategory.info)
        await state.update_data({'category': category})
    except Exception as e:
        log.warning(e)
        await not_correct_input(message)


@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.add_name)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.add_price)
@dp.callback_query(callback_factory.BackCall.filter(F.to == 'category_info'), AdminProduct.info)
async def cancel_add_product(call: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    category = data.get('category')
    await category_info(call, state, callback_factory.CategoryCall(pk=category.pk))


@dp.message(F.text.startswith('PRODUCT'), F.via_bot, AdminCategory.info)
async def product_info_inline(message: types.Message, state: FSMContext):
    product_pk: int = int(message.text.replace('PRODUCT ', ''))
    product: models.Product = models.Product.objects.filter(pk=product_pk).first()
    text = product.get_name()
    key = await inline.product_settings(back_to='category_info')
    await message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.info)
    await state.update_data({'product': product, 'page': 1})


@dp.callback_query(callback_factory.ProductCall.filter(), AdminCategory.info)
async def product_info(call: types.CallbackQuery, state: FSMContext, callback_data: callback_factory.ProductCall):
    product: models.Product = models.Product.objects.filter(pk=callback_data.pk).first()
    text = product.get_name()
    key = await inline.product_settings(back_to='category_info')
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.info)
    await state.update_data({'product': product, 'page': 1})


@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_help'
), AdminProduct.info)
@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_name'
), AdminProduct.info)
@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_desc'
), AdminProduct.info)
@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_price'
), AdminProduct.info)
@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_photo'
), AdminProduct.info)
async def change_product_call(call: types.CallbackQuery, state: FSMContext,
                              callback_data: callback_factory.AdminActionCall):
    match callback_data.action:
        case 'change_product_help':
            text = 'Введите новую технологичку: '
            await state.set_state(AdminProduct.change_help)
        case 'change_product_name':
            text = 'Введите новое название: '
            await state.set_state(AdminProduct.change_name)
        case 'change_product_desc':
            text = 'Введите новое описание: '
            await state.set_state(AdminProduct.change_desc)
        case 'change_product_price':
            text = 'Введите новую цену: '
            await state.set_state(AdminProduct.change_price)
        case _:
            text = 'Отправьте новое фото: '
            await state.set_state(AdminProduct.change_photo)
    key = await inline.cancel()
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)


@dp.message(AdminProduct.change_help)
@dp.message(AdminProduct.change_name)
@dp.message(AdminProduct.change_desc)
@dp.message(AdminProduct.change_price)
@dp.message(AdminProduct.change_photo)
async def change_product_help_text(message: types.Message, state: FSMContext):
    data = await state.get_data()
    product: models.Product = data.get('product')
    match await state.get_state():
        case AdminProduct.change_help:
            product.help = message.text
        case AdminProduct.change_name:
            if product.category.products.filter(name=message.text).count() > 0:
                return await not_correct_input(message, 'Продукт с таким названием уже имеется в данной категории.')
            else:
                product.name = message.text
        case AdminProduct.change_desc:
            product.description = message.text
        case AdminProduct.change_price:
            try:
                product.price = float(message.text)
            except Exception as e:
                log.warning(e)
                return await not_correct_input(message, 'Введите число.')
        case _:
            if message.photo:
                product.photo = message.photo[-1].file_id
            else:
                return await not_correct_input(message, 'Отправьте фото.')
    product.save()
    text = product.get_name()
    key = await inline.product_settings('category_info')
    await message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.info)
    await state.update_data({'product': product})


@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.change_price)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.change_desc)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.change_name)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.change_help)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.change_photo)
@dp.callback_query(callback_factory.BackCall.filter(F.to == 'product_info'), AdminProduct.consumable)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.consumable_amount)
async def cancel_change_product(call: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    product: models.Product = data.get('product')
    await product_info(call, state, callback_factory.ProductCall(pk=product.pk))


@dp.callback_query(callback_factory.AdminActionCall.filter(F.action == 'change_product_display'), AdminProduct.info)
async def change_product_display(call: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    product: models.Product = data.get('product')
    product.is_visible = False if product.is_visible else True
    product.save()
    await product_info(call, state, callback_factory.ProductCall(pk=product.pk))


@dp.callback_query(callback_factory.AdminActionCall.filter(
    F.action == 'change_product_compound'
), AdminProduct.info)
@dp.callback_query(callback_factory.ActionCall.filter(F.action == 'cancel'), AdminProduct.consumable_amount)
async def change_product_compound(call: types.CallbackQuery, state: FSMContext):
    text = 'Выберите нужный расходник: '
    key = inline.ConsumablePaginator(
        dp,
        before_pagination_keyboards=[await inline.find_keyboard('Расходники', 'consumable')],
        after_pagination_keyboards=[await inline.back('product_info')]
    )
    try:
        await call.message.edit_text(text, reply_markup=key.as_markup())
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key.as_markup())
    await state.set_state(AdminProduct.consumable)


@dp.message(F.text.startswith('CONSUMABLE'), F.via_bot, AdminProduct.consumable)
async def product_info_consumable_choice_inline(message: types.Message, state: FSMContext):
    consumable_pk: int = int(message.text.replace('CONSUMABLE ', ''))
    consumable: models.Consumable = models.Consumable.objects.filter(
        pk=consumable_pk
    ).first()
    text = f'Введите кол-во {consumable.unit} для {consumable.name}: '
    key = await inline.cancel()
    await message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.consumable_amount)
    await state.update_data({'consumable': consumable})


@dp.callback_query(callback_factory.ConsumableCall.filter(), AdminProduct.consumable)
async def product_info_consumable_choice(call: types.CallbackQuery, state: FSMContext,
                                         callback_data: callback_factory.ConsumableCall):
    consumable: models.Consumable = models.Consumable.objects.filter(
        pk=callback_data.pk
    ).first()
    text = f'Введите кол-во {consumable.unit} для {consumable.name}: '
    key = await inline.cancel()
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.consumable_amount)
    await state.update_data({'consumable': consumable})


@dp.message(AdminProduct.consumable_amount)
async def set_consumable_amount_to_product(message: types.Message, state: FSMContext):
    data = await state.get_data()
    product: models.Product = data.get('product')
    consumable: models.Consumable = data.get('consumable')
    try:
        float_value = float(message.text)
    except Exception as e:
        log.warning(e)
        return await not_correct_input(message, 'Введите число.')

    compound = models.Compound.objects.filter(
        product=product,
        consumable=consumable
    ).first()
    if float_value == 0.0:
        if compound:
            compound.delete()
    else:
        if compound:
            compound.count = float_value
            compound.save()
        else:
            models.Compound.objects.create(
                product=product,
                consumable=consumable,
                count=float_value
            )
    text = product.get_name()
    key = await inline.product_settings('category_info')
    await message.answer(text, reply_markup=key)
    await state.set_state(AdminProduct.info)
    await state.update_data({'product': product})
